import json
import time
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Query
from fastapi.middleware.cors import CORSMiddleware
from typing import List, Dict

import redis
from redis import Redis


# 创建FastAPI应用
app = FastAPI(title="用户-用户实时聊天系统")


# 初始化Redis连接（根据实际Redis地址配置）
redis_client: Redis = redis.Redis(
    host="localhost",  # Redis服务器地址
    port=6379,         # 端口
    db=0,              # 数据库编号
    decode_responses=True  # 自动解码为字符串（方便处理JSON）
)

# 跨域配置（允许前端调用）
origins = [
    "http://localhost",
    "http://localhost:5173",  # 前端常用开发端口（如Vue/React）
    "http://127.0.0.1:5173"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 全局连接管理
active_connections: List[WebSocket] = []  # 所有活跃连接
client_connections: Dict[str, WebSocket] = {}  # 客户端唯一标识 -> 连接映射（key: client_id）


async def send_json(websocket: WebSocket, data: dict):
    """向指定连接发送JSON格式消息"""
    await websocket.send_text(json.dumps(data))


@app.websocket("/ws/{client_id}")
async def chat_websocket(
    websocket: WebSocket,
    client_id: str,
    nickname: str | None = Query(None),
    client_name: str | None = Query(None)
):
    nickname = nickname or client_name or "匿名"
    # 1. 接受连接
    await websocket.accept()

    # 3. 记录连接
    active_connections.append(websocket)
    client_connections[client_id] = websocket
    print(f"用户[{client_id}({nickname})]已连接，当前在线: {len(active_connections)}人")

    # 4. 发送连接成功通知
    await send_json(websocket, {
        "type": "system",
        "message": "连接成功，可开始聊天",
        "online_count": len(active_connections)
    })

    redsi_key = f"offline:messages:{client_id}"
    offline_messages = redis_client.lrange(redsi_key, 0, -1)

    if offline_messages:
        for message in offline_messages:
            msg = json.loads(message)
            await send_json(websocket,{
                "type": "chat",
                "from": msg["from"],
                "from_nickname": msg["from_nickname"],
                "content": msg["content"],
                "time": msg["time"],
                "is_offline":True

            })
            redis_client.delete(redsi_key)
            print(f"用户[{client_id}({nickname})]已连接，当前在线: {len(active_connections)}人")


    try:
        # 5. 循环接收消息
        while True:
            # 接收客户端发送的消息（格式：{"to": "目标client_id", "content": "消息内容"}）
            data = await websocket.receive_text()
            try:
                message = json.loads(data)
            except json.JSONDecodeError:
                await send_json(websocket, {"type": "error", "message": "消息格式错误，需为JSON"})
                continue


            if message.get("type") == "typing":
                # 解析目标用户的client_id
                target_client_id = message.get("to")  # 从消息中获取接收方ID

                # if not target_client_id:  # 防止缺少“to”字段导致错误
                #     await send_json(websocket, {"type": "error", "message": "缺少目标用户ID（to）"})
                #     continue
                # 检查目标用户是否在线

                if target_client_id in client_connections:
                    # 转发“正在输入”状态给目标用户
                    await send_json(client_connections[target_client_id], {
                        "type": "typing",
                        "from": client_id,  # 发送方ID
                        "from_nickname": nickname,  # 发送方昵称
                        "time": time.strftime("%Y-%m-%d %H:%M:%S")  # 当前时间
                    })
                # 跳过后续的普通聊天消息处理逻辑
                continue


            # 解析消息内容
            target_client_id = message["to"]  # 目标用户的client_id
            content = message["content"]  # 消息内容

            # 6. 定向发送消息给目标用户
            if target_client_id in client_connections:
                # 目标在线：发送消息
                target_websocket = client_connections[target_client_id]
                await send_json(target_websocket, {
                    "type": "chat",
                    "from": client_id,
                    "from_nickname": nickname,
                    "content": content,
                    "time": time.strftime("%Y-%m-%d %H:%M:%S")
                })
                # 给发送方返回"已送达"确认
                await send_json(websocket, {
                    "type": "success",
                    "message": f"消息已发送给 {target_client_id}",
                    "time": time.strftime("%Y-%m-%d %H:%M:%S")
                })
            else:
                # 目标不在线：提示发送方
                offine_msg = {
                    "from":client_id,
                    "from_nickname": nickname,
                    "content":content,
                    "time": time.strftime("%Y-%m-%d %H:%M:%S"),
                    "msg_id":f"msg+{int(time.time()*1000)}"
                }
                redis_key = f"offline:messages:{target_client_id}"
                redis_client.lpush(redis_key, json.dumps(offine_msg))
                redis_client.expire(redis_key, 60*60*24*7)
                await send_json(websocket, {
                    "type": "success",
                    "message": f"用户{target_client_id}不在线，消息已保存到离线消息列表",
                })


    except WebSocketDisconnect:
        # 7. 处理断开连接
        active_connections.remove(websocket)
        if client_id in client_connections:
            del client_connections[client_id]
        print(f"用户[{client_id}({nickname})]已断开，当前在线: {len(active_connections)}人")
    except Exception as e:
        # 捕获其他异常
        print(f"连接异常[{client_id}]: {str(e)}")


# 测试用接口（检查服务是否启动）
@app.get("/")
def index():
    return {
        "status": "success",
        "message": "用户-用户聊天WebSocket服务已启动",
        "websocket连接示例": "ws://127.0.0.1:8000/ws/用户A的ID?nickname=张三",
        "发送消息格式": '{"to": "用户B的ID", "content": "你好！"}'
    }


if __name__ == "__main__":
    import uvicorn

    uvicorn.run("chat_wedsocket_lx:app", host="127.0.0.1", port=8002, reload=True)